/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	StructuredBuffer.cpp
*
*	Comments	-	See StructuredBuffer.h
*
**************************************************************************************/
#include "../Include/StructuredBuffer.h"
#include "../Include/Engine.h"
#include "../Include/AsyncLoadResource.h"

namespace Pulse
{
	StructuredBufferDesc::StructuredBufferDesc( void )
	{
		SetDefault();
	}

	void StructuredBufferDesc::SetDefault( void )
	{
		m_structureSize = 0;
		m_numElements = 0;
		m_usage = EBufferUsage::UNKNOWN;
		m_pData = PSX_NULL;

		m_srvDesc.SetDefaults();
		m_uavDesc.SetDefaults();
	}

	void StructuredBufferDesc::Set( SIZE_T structureSize, SIZE_T numElements, EBufferUsage::Type usage, void *pInitialData )
	{
		m_structureSize = structureSize;
		m_numElements = numElements;
		m_usage = usage;
		m_pData = pInitialData;

		{
			BufferInfoSRV info;

			info.firstElement = 0;
			info.numElements = numElements;

			m_srvDesc.SetDefaults();
			m_srvDesc.Set( EGraphicsFormat::UNKNOWN, &info );
		}

		{
			BufferInfoUAV info;

			info.firstElement = 0;
			info.numElements = numElements;
			//info.flags = bAllowRawView ? EBufferUAVFlags::RAW : 0;
			info.flags = 0;

			m_uavDesc.SetDefaults();
			m_uavDesc.Set( EGraphicsFormat::UNKNOWN, &info );
		}

	}

	StructuredBuffer::StructuredBuffer( void )
	{
	}

	StructuredBuffer::~StructuredBuffer( void )
	{

	}

	BOOL StructuredBuffer::CreateResource( BaseResDesc *pDesc )	
	{
		if ( pDesc->GetType() != GetType() )
			return FALSE;

		if ( ((StructuredBufferDesc*)pDesc)->m_usage == EBufferUsage::STATIC &&
			((StructuredBufferDesc*)pDesc)->m_pData == PSX_NULL )
		{
			PSX_PushError( "Can't create static structured buffer without providing an initial data." );
			return FALSE;
		}

		m_desc = *((StructuredBufferDesc*)pDesc);

		if ( PerformInitialize( m_desc.IsAsyncLoad() ) == EErrorCode::OKAY )
			SetFlagCreated();

		return IsCreated();	
	}

	BOOL StructuredBuffer::DestroyResource( void )				
	{
		UnloadResource();
		SetFlagDestroyed();	
		return TRUE;	
	}

	BOOL StructuredBuffer::LoadResource( void )				
	{ 
		if ( Initialize() == EErrorCode::OKAY )
		{
			SetFlagLoaded();	
			return TRUE;	
		}

		return FALSE;
	}

	BOOL StructuredBuffer::UnloadResource( void )				
	{
		Cleanup();
		SetFlagUnloaded();
		return FALSE;	
	}

	EErrorCode::Type StructuredBuffer::Initialize( void )
	{
		Renderer *pRenderer = Engine::GetInstance()->GetRenderer();
		ID3D11Device *pDevice = (ID3D11Device*)pRenderer->GetNakedDevice();

		HRESULT hr;
		D3D11_BUFFER_DESC desc;

		desc.ByteWidth				= (UINT)m_desc.m_structureSize * m_desc.m_numElements;
		desc.BindFlags				= D3D11_BIND_SHADER_RESOURCE /*| D3D11_BIND_UNORDERED_ACCESS*/; // Dynamic usage cannot let gpu write on resource
		desc.MiscFlags				= D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
		desc.StructureByteStride	= (UINT)m_desc.m_structureSize;

		if ( m_desc.m_usage == EBufferUsage::DYNAMIC )
		{
			desc.Usage = D3D11_USAGE_DYNAMIC;
			desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		}
		else if ( m_desc.m_usage == EBufferUsage::STATIC )
		{
			desc.Usage = D3D11_USAGE_IMMUTABLE;
			desc.CPUAccessFlags = 0;
		}
		else if ( m_desc.m_usage == EBufferUsage::DEFAULT )
		{
			desc.Usage = D3D11_USAGE_DEFAULT;
			desc.CPUAccessFlags = 0;
			desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; // Default, gpu can write on this resource
		}
		else if ( m_desc.m_usage == EBufferUsage::STAGING )
		{
			desc.Usage = D3D11_USAGE_STAGING;
			desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
		}
		else
		{
			PSX_PushError( "Not yet implemented." );
		}

		if ( m_desc.m_pData )
		{
			D3D11_SUBRESOURCE_DATA data;
			data.pSysMem = m_desc.m_pData;
			data.SysMemPitch = 0;
			data.SysMemSlicePitch = 0;

			hr = pDevice->CreateBuffer( &desc, &data, &m_pBuffer );
		}
		else
		{
			hr = pDevice->CreateBuffer( &desc, PSX_NULL, &m_pBuffer );
		}

		m_desc.m_pData = PSX_NULL;

		if ( FAILED(hr) )
		{
			PSX_PushError( "Failed to create D3D11 structured buffer." );
			return EErrorCode::GRAPHICS;
		}

		CreateResourceViews( m_pBuffer, desc.BindFlags, PSX_NULL, &m_desc.m_srvDesc, &m_desc.m_uavDesc );

		return EErrorCode::OKAY;
	}

	void StructuredBuffer::Cleanup( void )
	{
		ReleaseResourceViews();
		ReleaseBuffer();
	}
}